package com.util.vmwareops;
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.HashSet;
import java.util.LinkedList;
import java.sql.Driver;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import com.vmware.vim25.RuntimeFault;
import com.vmware.vim25.mo.ServiceInstance;

import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPFaultException;

public class VCConnectionPool implements ConnectionPool<ServiceInstance> {
    private LinkedBlockingQueue<ServiceInstance> freeConnection;
    private LinkedBlockingQueue<ServiceInstance> usedConnection;
    private URL url;
    private int port;
    private String username;
    private String password;
    private int maxConnect;
    private int minConnect;
    final boolean DEBUG = false;
    private long lastClearClosedConnection;
    public static long CHECK_CLOSED_CONNECTION_TIME = 6000000; // 10min
    public static long WAIT_TIME = 3000000; // 5min

    // 构造函数
    public VCConnectionPool(String url, int port, String username, String password) {
        this(url, port, username, password, 2, 1);
    }

    // 构造函数
    public VCConnectionPool(String url, int port, String username, String password, int maxConnect, int minConnect) {
        try {
            this.url = new URL("https://"+url+":"+port+"/sdk");
            this.port = port;
            this.username = username;
            this.password = password;
            this.freeConnection = new LinkedBlockingQueue<ServiceInstance>();
            this.usedConnection = new LinkedBlockingQueue<ServiceInstance>();
            this.lastClearClosedConnection = System.currentTimeMillis();
            this.maxConnect = maxConnect;
            this.minConnect = minConnect;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Override
    public boolean isClosed(ServiceInstance si) {
        if(si == null) {
            return true;
        }
        if(
                si.getServerConnection() != null &&
                        si.getServerConnection().getServiceInstance() == null &&
                        si.getServerConnection().getVimService() == null
        ) {
            System.out.println("Connection closed");
            return true;
        }

        if(si.getSessionManager() != null && si.getSessionManager().getCurrentSession() == null) {
            System.out.println("Connection closed");
            return true;
        }

        try {
            si.currentTime();
        } catch(RuntimeFault e) {
            return true;
        } catch (RemoteException e) {
            return true;
        }
        return false;
    }

    // 获取vcneter连接
    @Override
    public synchronized ServiceInstance getConnection() throws Exception {

        // 从空闲的连接池中获取一个未使用的连接
        while(this.freeConnection.size() > 0) {
            try {
                ServiceInstance si = (ServiceInstance)this.freeConnection.poll();
                if(this.isClosed(si)) {
                    continue;
                }
                System.out.println("Get Vc Connection Pool By Stock");
                this.usedConnection.add(si);
                return si;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //确保有可用的连接
        this.ensureFreeConnectionPool();

        ServiceInstance si = null;
        if(this.freeConnection.size() <= 0) {
            wait();
        }
        si = this.getConnection();
        return si;

    }
    private void ensureFreeConnectionPool() {

        // 如果有可用的连接，直接返回
        if(this.freeConnection.size() > 0) {
            return;
        }
        // 如果空闲连接池为空，需要新建一些连接共应用使用
        // 如果当前已经建立的连接超过了定义的最大连接数，则返回null，表示没有可用的连接
        // 如果还有空间可以建立连接，则获取一个要建立的连接数newCount，newCount+已经建立的连接需要小于最大的连接数限制
        int currentConnectionCount = this.usedConnection.size() + this.freeConnection.size();

        if(currentConnectionCount >= this.maxConnect) {
            return;
        }
        int newCount = this.getIncreasingConnection();
        if(newCount > this.maxConnect - currentConnectionCount) {
            newCount = this.maxConnect - currentConnectionCount;
        }

        if(newCount + currentConnectionCount < this.minConnect) {
            newCount = this.minConnect - currentConnectionCount;
        }

        // 新建可用的连接加入到空闲连接池中
        ServiceInstance con = null;
        for(int i=0;i<newCount;i++) {
            System.out.println("Get Connection Pool By New");
            con = this.getNewConnection();
            if(con != null ){
                this.freeConnection.offer(con);
            }
        }
    }
    private ServiceInstance getNewConnection() {
        try {
            System.out.println("New Service Instance ing");
            ServiceInstance serviceInstance= new ServiceInstance(this.url,this.username,this.password,true);
            return serviceInstance;
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        return null;
    }
    private int getIncreasingConnection() {
        int count = (this.usedConnection.size() + this.freeConnection.size()) / 5;
        return count >= 1 ? count : 1;
    }
    private void clearClosedConnection() {
        long time = System.currentTimeMillis();
        if(time - this.lastClearClosedConnection < CHECK_CLOSED_CONNECTION_TIME) {
            return;
        }
        this.lastClearClosedConnection = time;
        Iterator iterator = this.freeConnection.iterator();
        while(iterator.hasNext()) {
            ServiceInstance si = (ServiceInstance) iterator.next();
            try {
                if(this.isClosed(si)) {

                    iterator.remove();
                }
            } catch (Exception e) {
                iterator.remove();
                e.printStackTrace();
            }
        }

    }
    private int closePool(Iterator iterator) {
        int count = 0;
        while(iterator.hasNext()) {
            try {
                ((ServiceInstance)iterator.next()).getServerConnection().logout();
                count++;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return count;
    }
    @Override
    public int closeAll() {
        int count = 0;
        Iterator iterator = this.usedConnection.iterator();
        count += this.closePool(iterator);
        this.usedConnection.clear();

        iterator = this.freeConnection.iterator();
        count += this.closePool(iterator);
        this.freeConnection.clear();

        return count;
    }
    @Override
    public synchronized void close(ServiceInstance con)
    {
        boolean exist = this.usedConnection.remove(con);
        if(exist) {
            this.freeConnection.offer(con);
            notifyAll();
        }
        System.out.println("[CLOSE] VC used " + this.usedConnection.size() + "; free " + this.freeConnection.size());
    }

}
